﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using System.Globalization;

namespace System.Windows.Art
{
    public struct XYZColor : IColor
    {
        private Double scAlpha, scX, scY, scZ;

        private XYZColor(Double scAlpha, Double scX, Double scY, Double scZ)
        {
            this.scAlpha = scAlpha;
            this.scX = scX;
            this.scY = scY;
            this.scZ = scZ;
        }

        public static XYZColor FromScXYZ(Double scAlpha, Double scX, Double scY, Double scZ)
        {
            return new XYZColor(scAlpha, scX, scY, scZ);
        }

        public Color ToColor() { return ColorSpaceConverter.ToColor(this); }
        public ColorSpaceBase Space { get { return ColorSpaces.XYZ; } }

        public Double GetValue(ColorChannel channel)
        {
            var space = ColorSpaces.XYZ;
            if (channel == space.Alpha) return this.scAlpha;
            else if (channel == space.X) return this.scX;
            else if (channel == space.Y) return this.scY;
            else if (channel == space.Z) return this.scZ;
            else throw new InvalidOperationException();
        }

        public void SetValue(ColorChannel channel, Double value)
        {
            var space = ColorSpaces.XYZ;
            if (channel == space.Alpha) this.scAlpha = value;
            else if (channel == space.X) this.scX = value;
            else if (channel == space.Y) this.scY = value;
            else if (channel == space.Z) this.scZ = value;
            else throw new InvalidOperationException();
        }

        public Double GetClassicValue(ColorChannel channel)
        {
            var space = ColorSpaces.XYZ;
            if (channel == space.Alpha) return this.Alpha;
            else if (channel == space.X) return this.X;
            else if (channel == space.Y) return this.Y;
            else if (channel == space.Z) return this.Z;
            else throw new InvalidOperationException();
        }

        public void SetClassicValue(ColorChannel channel, Double value)
        {
            var space = ColorSpaces.XYZ;
            if (channel == space.Alpha) this.Alpha = (Byte)value;
            else if (channel == space.X) this.X = (Int32)value;
            else if (channel == space.Y) this.Y = (Int32)value;
            else if (channel == space.Z) this.Z = (Int32)value;
            else throw new InvalidOperationException();
        }

        public Byte Alpha
        {
            get
            {
                if (this.scAlpha < 0) return 0;
                else if (this.scAlpha > 1) return 255;
                else return (Byte)Math.Round(255 * this.scAlpha);
            }
            set { this.scAlpha = value / 255.0; }
        }

        /// <summary>
        /// Classic Alpha scaled to [0; 1].
        /// <remarks>Linear, unbounded.</remarks>
        /// </summary>
        public Double ScAlpha
        {
            get { return this.scAlpha; }
            set { this.scAlpha = value; }
        }

        public Int32 X
        {
            get { return (Int32)Math.Round(100 * this.scX); }
            set { this.scX = value / 100.0; }
        }

        public Double ScX
        {
            get { return this.scX; }
            set { this.scX = value; }
        }

        public Int32 Y
        {
            get { return (Int32)Math.Round(100 * this.scY); }
            set { this.scY = value / 100.0; }
        }

        public Double ScY
        {
            get { return this.scY; }
            set { this.scY = value; }
        }

        public Int32 Z
        {
            get { return (Int32)Math.Round(100 * this.scZ); }
            set { this.scZ = value / 100.0; }
        }

        public Double ScZ
        {
            get { return this.scZ; }
            set { this.scZ = value; }
        }

        public override String ToString()
        {
            return this.ToString("g", CultureInfo.CurrentCulture);
        }

        public String ToString(String format)
        {
            return this.ToString(format, CultureInfo.CurrentCulture);
        }

        public String ToString(String format, IFormatProvider provider)
        {
            var builder = new StringBuilder();

            builder.Append(this.Space.ToString()).Append('#');
            builder.Append(this.scAlpha.ToString(format, provider)).Append("; ");
            builder.Append(this.scX.ToString(format, provider)).Append("; ");
            builder.Append(this.scY.ToString(format, provider)).Append("; ");
            builder.Append(this.scZ.ToString(format, provider));

            return builder.ToString();
        }

        public Object Clone()
        {
            return this.MemberwiseClone();
        }
    }
}
